package com.hly.tool.login;

import org.json.JSONArray;
import org.json.JSONObject;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import org.xml.sax.InputSource;
import org.xml.sax.SAXException;

import javax.net.ssl.HttpsURLConnection;
import javax.net.ssl.SSLContext;
import javax.net.ssl.TrustManager;
import javax.net.ssl.X509TrustManager;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
import java.io.*;
import java.net.HttpURLConnection;
import java.net.URL;
import java.net.URLConnection;
import java.net.URLEncoder;
import java.nio.charset.Charset;
import java.nio.charset.StandardCharsets;
import java.security.KeyManagementException;
import java.security.NoSuchAlgorithmException;
import java.security.SecureRandom;
import java.security.cert.X509Certificate;
import java.util.*;

public class Passport
{
    final static String kernelServlet = "/servlet/MatrixXMLServlet";


    /**
     * 获取 ticket
     *
     * @param host     url 地址
     * @param user     用户名
     * @param password 密码
     * @return
     * @throws Exception
     */
    public static String getTicket(String host, String user, String password) throws Exception
    {

        HttpURLConnection conTemp = getHttpConnection(host + kernelServlet, false);
        int respTemp = conTemp.getResponseCode();
        if (respTemp != 302)
        {
            throw new Exception("Required CAS Redirect not found");
        }
        String redirectUrl = conTemp.getHeaderField("Location");
        HttpURLConnection conCAS = getHttpConnection(redirectUrl, false);
        Map<String, String> cookiesCAS = getCASCookies(conCAS);
        String jSessionId = cookiesCAS.get("JSESSIONID");
        String authParamsCAS = getAuthParams(conCAS);
        JSONObject jsonCAS = new JSONObject(authParamsCAS);
        String lt = (String) jsonCAS.get("lt");

        String loginUrlCAS = (String) jsonCAS.get("url");

        HttpURLConnection conCASLogin = getHttpConnection(loginUrlCAS, false);
        conCASLogin.setRequestProperty("Cookie", "JSESSIONID=" + jSessionId);
        conCASLogin.setRequestMethod("POST");
        conCASLogin.setDoOutput(true);

        Properties casUrlParamProperties = new Properties();
        casUrlParamProperties.put("lt", lt);
        casUrlParamProperties.put("username", user);
        casUrlParamProperties.put("password", password);
        String casUrlParams = encodeUrlParams(casUrlParamProperties);
        DataOutputStream wr = new DataOutputStream(conCASLogin.getOutputStream());
        Throwable localThrowable2 = null;
        try
        {
            wr.writeBytes(casUrlParams);
            wr.flush();
        } catch (Throwable localThrowable1)
        {
            localThrowable2 = localThrowable1;
            throw localThrowable1;
        } finally
        {
            if (wr != null)
            {
                if (localThrowable2 != null)
                {
                    try
                    {
                        wr.close();
                    } catch (Throwable x2)
                    {
                        localThrowable2.addSuppressed(x2);
                    }
                } else
                {
                    wr.close();
                }
            }
        }
        int respCodeCASLogin = conCASLogin.getResponseCode();
        if (respCodeCASLogin == 302)
        {
            String redirectUrlFromCASLogin = conCASLogin.getHeaderField("Location");
            if (redirectUrlFromCASLogin.contains("?ticket="))
            {
                String ticket = redirectUrlFromCASLogin.substring(redirectUrlFromCASLogin.lastIndexOf("?ticket="));
                return ticket;
            }
            throw new Exception("Required CAS Ticket not found");
        }
        String authParamsCASLogin = getAuthParams(conCASLogin);
        JSONObject jsonCASLogin = new JSONObject(authParamsCASLogin);
        JSONArray jsonArrayCASLogin = (JSONArray) jsonCASLogin.get("errorMsgs");
        String errorMsg = "";
        for (int i = 0; i < jsonArrayCASLogin.length(); i++)
        {
            JSONObject tmp = (JSONObject) jsonArrayCASLogin.get(i);
            errorMsg = errorMsg + tmp.get("defaultMessage");
        }
        if (errorMsg.isEmpty())
        {
            errorMsg = "Internal Server Error";
        }
        throw new Exception(errorMsg);
    }

    private static HttpURLConnection getHttpConnection(String sUrl, boolean followRedirect)
            throws IOException, NoSuchAlgorithmException, KeyManagementException
    {
        TrustManager[] trustAllCerts = new TrustManager[]{new X509TrustManager()
        {
            public X509Certificate[] getAcceptedIssuers()
            {
                return null;
            }

            public void checkClientTrusted(X509Certificate[] certs, String authType)
            {
            }

            public void checkServerTrusted(X509Certificate[] certs, String authType)
            {
            }
        }};

        // Install the all-trusting trust manager
        SSLContext sc = SSLContext.getInstance("TLS");
        sc.init(null, trustAllCerts, new SecureRandom());
        HttpsURLConnection.setDefaultSSLSocketFactory(sc.getSocketFactory());

        URL url = new URL(sUrl);
        HttpURLConnection con = (HttpURLConnection) url.openConnection();
        con.setInstanceFollowRedirects(followRedirect);
        return con;
    }

    private static Map<String, String> getCASCookies(URLConnection con)
    {
        Map<String, String> cookies = new HashMap<>();
        Map<String, List<String>> headers = con.getHeaderFields();
        for (Map.Entry<String, List<String>> entry : headers.entrySet())
        {
            String headerKey = entry.getKey();
            if ((headerKey != null) && (headerKey.equalsIgnoreCase("Set-Cookie")))
            {
                for (String headerValue : entry.getValue())
                {
                    if (headerValue != null)
                    {
                        String[] fields = headerValue.split(";\\s*");
                        String cookieValue = fields[0];
                        if ((cookieValue.startsWith("JSESSIONID")) || (cookieValue.startsWith("afs")) || (cookieValue.startsWith("CASTGC")))
                        {
                            String[] a = cookieValue.split("=", 2);
                            cookies.put(a[0], a[1]);
                        }
                    }
                }
                break;
            }
        }
        return cookies;
    }

    private static String encodeUrlParams(Properties p) throws UnsupportedEncodingException
    {
        StringBuilder sb = new StringBuilder();
        Enumeration<?> names = p.propertyNames();
        while (names.hasMoreElements())
        {
            String name = (String) names.nextElement();
            String value = p.getProperty(name);
            sb.append("&").append(URLEncoder.encode(name, "UTF-8")).append("=").append(URLEncoder.encode(value, "UTF-8"));
        }
        return sb.delete(0, 1).toString();
    }

    private String getAuthParams1(URLConnection con) throws ParserConfigurationException, SAXException, IOException
    {
        String authParams = null;
        DocumentBuilder db = DocumentBuilderFactory.newInstance().newDocumentBuilder();
        Document doc = db.parse(con.getInputStream());
        NodeList nodes = doc.getElementsByTagName("script");
        for (int i = 0; i < nodes.getLength(); i++)
        {
            Node node = nodes.item(i);
            if (node.getNodeType() == 1)
            {
                Element e = (Element) node;
                if ((e.getAttribute("id").equals("configData")) && (e.getAttribute("type").equals("application/json")))
                {
                    authParams = e.getTextContent();
                    break;
                }
            }
        }
        return authParams;
    }


    private static String getAuthParams(URLConnection con) throws ParserConfigurationException, SAXException, IOException
    {
        String authParams = null;
        String page = inputStreamToString(con.getInputStream(), StandardCharsets.UTF_8);
        page = page.replace("&egrave;", "").replace("&reg;", "").replace("sales to engineering.\">", "\"/>");
        DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
        dbf.setValidating(false);
        DocumentBuilder db = dbf.newDocumentBuilder();
        Document doc = db.parse(new InputSource(new StringReader(page)));
        NodeList nodes = doc.getElementsByTagName("script");

        for (int i = 0; i < nodes.getLength(); ++i)
        {
            Node node = nodes.item(i);
            if (node.getNodeType() == 1)
            {
                Element e = (Element) node;
                if (e.getAttribute("id").equals("configData") && e.getAttribute("type").equals("application/json"))
                {
                    authParams = e.getTextContent();
                    break;
                }
            }
        }

        return authParams;
    }

    private static String inputStreamToString(InputStream inputStream, Charset charset) throws IOException
    {
        StringWriter writer = new StringWriter();

        String var4;


        try
        {
            InputStreamReader reader = new InputStreamReader(inputStream, charset);

            try
            {
//                reader.transferTo(writer);
                int len = 0;
                while ((len = reader.read()) != -1)
                {
                    writer.write(len);
                }
                var4 = writer.toString();
            } catch (Throwable var8)
            {
                try
                {
                    reader.close();
                } catch (Throwable var7)
                {
                    var8.addSuppressed(var7);
                }

                throw var8;
            }

            reader.close();
        } catch (Throwable var9)
        {
            try
            {
                writer.close();
            } catch (Throwable var6)
            {
                var9.addSuppressed(var6);
            }

            throw var9;
        }

        writer.close();
        return var4;
    }
}
